"""
Author: Jian SuiYue kaohx0214@yeah.net
Date: 2025-05-15 10:35:00
LastEditTime: 2025-05-23 15:18:25
FilePath: LibraryManagerStudio/web/routes/book_api.py
"""
import os
from typing import Optional

import aiofiles
from fastapi import APIRouter, Depends, HTTPException, status, Query, Form, UploadFile, File
from fastapi.responses import JSONResponse
from loguru import logger
from sqlmodel import SQLModel

from .token_api import validate_jwt
from .utils import save_cover
from ..deps import get_db
from ...database import DBManager
from ...datamodel import Book, User, Category, Borrow, BookStatus, BorrowStatus

# 创建路由
router = APIRouter()

"""
API Sum:
method    Path(Contain Header)          RequestBody               Details
 get      /books/get/{book_id}                                    通过book_id 获得该book的详细信息
 post     /books/gets                   book_info: BookPageInfo   获取图书信息列表
 post     /books/new                    book_base: BookBase       创造一本新的书
 put      /books/update                 book: Book                对一本书的信息全部更改, 除了主键           
delete    /books/delete/{book_id}                                 删除一本书
"""


@router.get(path="/get")
async def get_book(
        login: str = Query(...),
        book_id: int = Query(..., ge=1),
        db: DBManager = Depends(get_db)
):
    """
    Get the book detail by book_id.

    GET /get?login={your_token}&book_id={your_book_id}
    Args:
        login: Token.id
        book_id: Book.id
        db: DBManager
    """
    validate_jwt(login, db)
    book = db.get(Book, filters={"id": book_id})
    if not book.status or not book.data:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Book ID: {book_id} Not found")
    if book.data[0].status != BookStatus.PUBLISH:
        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=f"Book wait pending or already rejected!")
    logger.info(f"{book.data[0]} {type(book.data[0])}")
    return {"status": True, "data": book.data[0]}


@router.post("/gets")
async def get_books(
        login: str = Query(...),
        page: int = Query(1, ge=1),
        size: int = Query(10, ge=1, le=100),
        db: DBManager = Depends(get_db)
):
    """
    Get the book list by page and size.

    POST /gets?login={your_token}&page={page}&size={size} 
    Args:
        login: Token.id
        page: table show current page
        size: table show size
        db: DBManager 
    """
    validate_jwt(login, db)
    result = db.get(Book, filters={"status": BookStatus.PUBLISH}, page_info={'page': page, 'size': size})
    if not result.status:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"UnExcepted Error: {result.message}")
    return JSONResponse(
        status_code=status.HTTP_200_OK,
        content={"status": True, "data": result.data, "message": "Get book list successful"}
    )


@router.get("/gets")
async def get_books_by_uid(
        login: str = Query(...),
        db: DBManager = Depends(get_db)
):
    """
    Get the book by uid -> to view: bookshelf
    
    Get /gets?login={login}
    Args:
        login: Token.id
        db: DBManager
    """
    uid = validate_jwt(login, db)
    res = db.get(Book, filters={"uid": uid}).data
    data = []
    for book in res:
        # if book.status == BookStatus.PUBLISH:
        #     data.append(book)
        data.append(book) # test
    return {"status": True, "data": data}

BOOKCOVER_PATH = os.path.join(
    os.path.dirname(
        os.path.dirname(os.path.abspath(__file__))
    ),
    'static', 'book'
)

@router.post("/new")
async def new_book(
        login: str = Query(...),
        uid: int = Query(...),
        cover: UploadFile = File(...),
        title: str = Form(...),
        author: str = Form(...),
        isbn: str = Form(None),
        category_id: int = Form(None),
        introduction: str = Form(None),
        description: str = Form(None),
        db: DBManager = Depends(get_db)
):
    """
    Create a new book.

    POST /gets?login={your_token}&page={page}&size={size}
    Args:
        login: Token.id
        uid: Book.uid
        cover: Book.cover
        title: Book.title
        author: Book.author
        isbn: Book.isbn
        category_id: Book.category_id
        introduction: Book.introduction
        description: Book.description
        db: DBManager
    """
    
    logger.info(f"get cover: {cover.filename}, title: {title}, author: {author}, isbn: {isbn}, category_id: {category_id}, introduction: {introduction}, description: {description}")
    
    validate_jwt(login, db)
    if isbn:
        book_exist = db.get(Book, filters={"isbn": isbn})
        if book_exist.data and book_exist.status:
            raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=f"Book ISBN is existed")

    # to judge the uid, isbn, category_id is existed in sqlite or not by its associated
    user_exist = db.get(User, filters={"id": uid})
    if not user_exist.status or not user_exist.data:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"User ID  not found")

    if category_id:
        category_exist = db.get(Category, filters={"category_id": category_id})
        if not category_exist.data or not category_exist.status:
            raise HTTPException(status_code=status.HTTP_409_CONFLICT,
                                detail=f"Book category not found")

    file_ext = cover.filename.split('.')[-1].lower()
    filename = f"{author}{title}.{file_ext}"
    save_path = os.path.join(BOOKCOVER_PATH, filename)

    # 异步保存文件
    try:
        async with aiofiles.open(save_path, 'wb') as f:
            await f.write(await cover.read())
    except Exception as e:
        logger.error(f"文件保存失败: {str(e)}")
        raise HTTPException(status_code=500, detail="头像保存失败")
    
    book = Book(uid=uid, isbn=isbn, title=title, cover=f'/static/book/{author}{title}.{file_ext}',
                author=author, description=description, introduction=introduction,
                category_id=category_id)

    result = db.upsert(book)
    if not result.status:
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"UnExcepted Error: {result.message}")

    return {"status": True, "message": "Create book successful"}


@router.put("/update")
async def update_book(
        login: str = Query(...),
        id: int = Form(...),
        cover: Optional[UploadFile] = File(None),
        isbn: Optional[str] = Form(None),
        title: Optional[str] = Form(None),
        author: Optional[str] = Form(None),
        description: Optional[str] = Form(None),
        category_id: Optional[int] = Form(None),
        status: Optional[BookStatus] = Form(None),
        db: DBManager = Depends(get_db)
):
    """
    Update book information.

    PUT /api/books/update?login={your_token}
    Args:
        login: Token.id
        id: Book.id
        cover: Book.cover
        isbn: Book.isbn
        title: Book.title
        author: Book.author
        description: Book.description
        category_id: Book.category_id
        status: Book.status
        db: DBManager
    """
    validate_jwt(login, db)

    book_exist = db.get(Book, filters={"id": id})
    if not book_exist.status or not book_exist.data:  # The book is not existed
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Book ID {id} not found")

    if category_id is not None:
        category_exist = db.get(Category, filters={"category_id": category_id})
        if not category_exist.data or not category_exist.status:
            raise HTTPException(status_code=status.HTTP_409_CONFLICT,
                                detail=f"Book category {category_id} not found")
    if isbn is not None:
        isbn_exist = db.get(Book, filters={"isbn": isbn})
        if isbn_exist.data and isbn_exist.status and isbn_exist.data[0].id != id:
            raise HTTPException(status_code=status.HTTP_409_CONFLICT,
                                detail=f"Book ISBN {isbn} is exist at other book")
    # Combine the new book with the original book
    origin = book_exist.data[0]
    updates = {
        "cover": save_cover(cover),
        "isbn": isbn,
        "title": title,
        "author": author,
        "description": description,
        "category_id": category_id,
        "status": status
    }

    for field, value in updates.items():
        if value is not None:  # Why do not write like "if not value", because mybae the k is a boolean object rather than none
            setattr(origin, field, value)

    response = db.upsert(Book(**origin))
    if not response.status:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"UnExpected Error: {response.message}")

    return JSONResponse(
        status_code=status.HTTP_200_OK,
        content={"status": True, "data": response.data[0], "message": "Update book successful"}
    )


@router.delete("/delete")
async def delete_book(
        login: str = Query(...),
        book_id: int = Query(..., ge=1),
        db: DBManager = Depends(get_db)
):
    """
    Delete a book.

    DELETE /delete?login={your_token}&book_id={book_id} 
    Args:
        login: Token.id
        book_id: Book.id
        db: DBManager
    """
    validate_jwt(login, db)

    book = db.get(Book, filters={"id": book_id})
    if not book.status or not book.data:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Book ID {book_id} not found")

    borrow_id = book.data[0]["borrow_id"]
    if borrow_id:  # if borrow_id exist we need to update the borrow status to return
        borrow = db.get(Borrow, filters={"id": borrow_id})
        if borrow.status and borrow.data:
            borrow.data[0]["status"] = BorrowStatus.RETURN
            db.upsert(Borrow(**borrow.data[0]))  # update the borrow status to return when delete the boo

    db.delete(Book, filters={"id": book_id})

    return JSONResponse(
        status_code=status.HTTP_200_OK,
        content={"status": True, "message": "Delete book successful"}
    )
